﻿using IntelliLock.Licensing;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Printing;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
namespace Yb
{
    /// <summary>
    /// 打印操作结果消息类
    /// </summary>
    public class YbMessage
    {
        /// <summary>
        /// 状态码: 0 表示成功，非0表示错误
        /// </summary>
        public int code { get; set; }

        /// <summary>
        /// 消息描述
        /// </summary>
        public string message { get; set; }

        /// <summary>
        /// 构造函数：成功时使用
        /// </summary>
        public YbMessage()
        {
            this.code = 0;
            this.message = "操作成功";
        }
       
        /// <summary>
        /// 构造函数：自定义消息
        /// </summary>
        /// <param name="code">状态码</param>
        /// <param name="message">消息内容</param>
        public YbMessage(int code, string message)
        {
            this.code = code;
            this.message = message;
        }

        /// <summary>
        /// 快捷方法：创建成功消息
        /// </summary>
        /// <param name="message">自定义成功消息</param>
        /// <returns>YbMessage实例</returns>
        public static YbMessage Success(string message = "操作成功")
        {
            return new YbMessage(0, message);
        }

        /// <summary>
        /// 快捷方法：创建错误消息
        /// </summary>
        /// <param name="message">错误消息</param>
        /// <returns>YbMessage实例</returns>
        public static YbMessage Error(string message)
        {
            return new YbMessage(-1, message);
        }

        /// <summary>
        /// 重写 ToString 方法，返回 JSON 格式字符串
        /// </summary>
        /// <returns>JSON 格式的字符串</returns>
        public override string ToString()
        {
            return $"{{\"code\":{code},\"message\":\"{message.Replace("\\", "\\\\").Replace("\"", "\\\"")}\"}}";
        }
    }
    internal class PrintPlug
    {
        private static event Action OnEnd;
         private static Config config;
        public static T GetConfig<T>(string key, T defaultValue = default(T))
        {
            if (config == null)
            {
                return defaultValue;
            }
            return config.Get<T>(key, defaultValue);
        }
        public static void SetConfig(Config c)
        {
            config = c;
        }

        private static Yb.SecureBool license = new SecureBool(false);
        public static YbMessage ActivateLicense()
        {
            switch (EvaluationMonitor.CurrentLicense.LicenseStatus)
            {
                case LicenseStatus.Licensed:
                    license.Value = true;
                    return new YbMessage(0, "已注册");
                case LicenseStatus.EvaluationExpired:
                    return new YbMessage(-1,"已过期");
                case LicenseStatus.HardwareNotMatched:
                    return new YbMessage(-1, "硬件不匹配");
                case LicenseStatus.LicenseFileNotFound:
                    return new YbMessage(-1, "未注册");
                case LicenseStatus.EvaluationMode:
                    return new YbMessage(-1, "试用版");
                case LicenseStatus.ServerValidationFailed:
                    return new YbMessage(-1, "服务器验证失败");
                case LicenseStatus.InvalidSignature:
                    return new YbMessage(-1, "无效签名");
                case LicenseStatus.NotChecked:
                    return new YbMessage(-1, "未检测");
                case LicenseStatus.Reactivated:
                    return new YbMessage(-1, "重新激活");
                case LicenseStatus.Deactivated:
                    return new YbMessage(-1, "无效");
                default:
                    return new YbMessage(-1, "未验证成功");
            }
        }
        public class DocConfig
        {
            public int width;
            public int height;
            public string orientation;
        }

        public class Template
        {
            public DocConfig config;
            public JArray nodes;
        }

        public class YbTemplate
        {
            public string TaskId;
            public Template Template;
            public string url;
            public JObject data;
            public string PrintName;
        }

        private class printData
        {
            public PrintDocument doc;
            public PaperSize page;
            public YbTemplate template;
            private bool _hasNextPage = false;
            /// <summary>
            /// 通用安全获取节点值方法（支持点分路径）
            /// </summary>
            private T GetValue<T>(JObject node, string path, T defaultValue = default(T))
            {
                if (node == null || string.IsNullOrEmpty(path))
                    return defaultValue;

                JToken current = node;
                string[] parts = path.Split('.');

                foreach (string part in parts)
                {
                    if (current is JObject obj && obj.TryGetValue(part, out JToken next))
                    {
                        current = next;
                    }
                    else
                    {
                        return defaultValue; // 路径中断或属性不存在
                    }
                }

                if (current == null || current.Type == JTokenType.Null)
                    return defaultValue;

                try
                {
                    return current.Value<T>();
                }
                catch
                {
                    return defaultValue; // 类型转换失败
                }
            }

            /// <summary>
            /// 将字符串中的 <%=expression%> 模板表达式替换为对应的数据值
            /// </summary>
            private string ProcessTemplateString(string template)
            {
                if (string.IsNullOrEmpty(template) || this.template?.data == null)
                    return template;

                var regex = new Regex(@"<%=\s*([^%>]+?)\s*%>");
                return regex.Replace(template, match =>
                {
                    string expression = match.Groups[1].Value.Trim();
                    try
                    {
                        JToken result = EvaluateExpression(expression);
                        return result?.ToString() ?? "";
                    }
                    catch (Exception ex)
                    {
                        return $"[解析错误: {ex.Message}]";
                    }
                });
            }

            /// <summary>
            /// 评估表达式，直接使用 this.template.data 作为上下文
            /// </summary>
            private JToken EvaluateExpression(string expression)
            {
                if (string.IsNullOrEmpty(expression))
                    return null;

                var parts = SplitExpression(expression);
                JToken current = this.template.data;

                foreach (var part in parts)
                {
                    if (current == null) break;

                    if (part.StartsWith("[") && part.EndsWith("]"))
                    {
                        string indexStr = part.Substring(1, part.Length - 2);
                        if (int.TryParse(indexStr, out int index))
                        {
                            if (current is JArray array && index >= 0 && index < array.Count)
                            {
                                current = array[index];
                            }
                            else if (current is JValue jValue && jValue.Type == JTokenType.String)
                            {
                                string strValue = jValue.Value<string>();
                                if (index >= 0 && index < strValue.Length)
                                {
                                    current = new JValue(strValue[index].ToString());
                                }
                                else
                                {
                                    current = null;
                                }
                            }
                            else
                            {
                                current = null;
                            }
                        }
                        else
                        {
                            current = null;
                        }
                    }
                    else
                    {
                        if (current is JObject obj && obj.ContainsKey(part))
                        {
                            current = obj[part];
                        }
                        else
                        {
                            current = null;
                        }
                    }
                }

                return current;
            }

            /// <summary>
            /// 分割表达式
            /// </summary>
            private List<string> SplitExpression(string expression)
            {
                var parts = new List<string>();
                var current = new StringBuilder();
                bool inBracket = false;

                for (int i = 0; i < expression.Length; i++)
                {
                    char c = expression[i];

                    if (c == '[' && !inBracket)
                    {
                        if (current.Length > 0)
                        {
                            parts.Add(current.ToString());
                            current.Clear();
                        }
                        inBracket = true;
                        current.Append(c);
                    }
                    else if (c == ']' && inBracket)
                    {
                        current.Append(c);
                        parts.Add(current.ToString());
                        current.Clear();
                        inBracket = false;
                    }
                    else if (c == '.' && !inBracket)
                    {
                        if (current.Length > 0)
                        {
                            parts.Add(current.ToString());
                            current.Clear();
                        }
                    }
                    else
                    {
                        current.Append(c);
                    }
                }

                if (current.Length > 0)
                {
                    parts.Add(current.ToString());
                }

                return parts;
            }

            // 新增字段：是否等待打印完成
            public bool WaitForPrintCompletion { get; set; } = false;

            // 用于同步等待的 ManualResetEvent
            private System.Threading.ManualResetEvent _printCompleteEvent = new System.Threading.ManualResetEvent(false);
            /// <summary>
            /// 私有打印函数 - 负责实际的打印逻辑
            /// </summary>
            /// <returns>打印操作结果</returns>
            private YbMessage ExecutePrint()
            {
                try
                {
                    if (this.doc == null)
                        return new YbMessage(-10, "打印文档对象未初始化");
                    if (this.page == null)
                        return new YbMessage(-11, "纸张尺寸未设置");
                    if (this.template == null || this.template.Template == null)
                        return new YbMessage(-12, "打印模板数据为空");

                        // 静默打印：使用 template.PrintName
                        if (!string.IsNullOrEmpty(this.template.PrintName))
                        {
                            this.doc.PrinterSettings.PrinterName = this.template.PrintName;
                        }

                    // ✅【关键修复】在这里提前设置纸张方向！
                    bool isLandscape = this.template.Template.config.orientation?.ToLower() == "landscape";
                    this.doc.DefaultPageSettings.Landscape = isLandscape;

                    // 应用纸张设置
                    this.doc.DefaultPageSettings.PaperSize = this.page;
                    this.doc.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0);

                    // 绑定事件并打印
                    this.doc.PrintPage += Doc_PrintPage;
                    this.doc.EndPrint += End_PrintPage;
                    this.doc.Print();
                    // 如果需要等待打印完成，则阻塞当前线程直到打印结束
                    if (WaitForPrintCompletion)
                    {
                        _printCompleteEvent.WaitOne(); // 等待 EndPrint 触发 Set()
                        _printCompleteEvent.Dispose(); // 清理资源
                        return new YbMessage(0, "打印任务已完成");
                    }
                    return new YbMessage(0, "打印任务已启动");
                }
                catch (Exception ex)
                {
                    return new YbMessage(-8, $"打印执行失败: {ex.Message}");
                }
            }

            private void End_PrintPage(object sender, PrintEventArgs e)
            {

                    if (WaitForPrintCompletion)
                    {
                        _printCompleteEvent.Set(); // 通知等待线程打印已完成
                }
            }

            private void DrawNode(Graphics g, JObject node, float offsetX = 0, float offsetY = 0)
            {
                if (node["rect"] == null) return;

                // 获取节点位置和尺寸（毫米单位）
                float x = GetValue<float>(node, "rect.x", 0f) + offsetX;
                float y = GetValue<float>(node, "rect.y", 0f) + offsetY;
                float width = GetValue<float>(node, "rect.width", 0f);
                float height = GetValue<float>(node, "rect.height", 0f);

                // 绘制背景色
                DrawBackground(g, node, x, y, width, height);

                // 绘制边框
                RectangleF newrect = DrawBorder(g, node, x, y, width, height);
                x = newrect.Left; y = newrect.Top;
                width = newrect.Width; height = newrect.Height;
                // 绘制文本（如果存在）
                if (node["type"] != null)
                {
                    string type = GetValue<string>(node, "type", "");
                    switch (type)
                    {
                        case "label":
                            string rawText = GetValue<string>(node, "text", "");
                            string processedText = ProcessTemplateString(rawText);
                            DrawLabel(g, node, processedText, x, y, width, height);
                            break;
                        case "qrcode":
                            if (node["text"] != null)
                            {
                                string qrrawText = GetValue<string>(node, "text", "");
                                string qrprocessedText = ProcessTemplateString(qrrawText);
                                DrawQrCode(g, node, qrprocessedText, x, y, width, height);
                            }
                            break;
                        case "barcode":
                            if (node["text"] != null)
                            {
                                string brrawText = GetValue<string>(node, "text", "");
                                string brprocessedText = ProcessTemplateString(brrawText);
                                DrawBarcode(g, node, brprocessedText, x, y, width, height);
                            }
                            break;
                        case "box":
                            // 'box' 类型通常只绘制背景和边框，没有额外内容
                            // DrawBox(g, node, x, y, width, height); // 如果需要特殊逻辑，可以调用，否则省略
                            break;
                        case "line":
                            DrawLine(g, node, x, y, width, height);
                            break;
                        case "image":
                            if (node["text"] != null)
                            {
                                string imageUrl = GetValue<string>(node, "text", "");
                                DrawImage(g, node, imageUrl, x, y, width, height);
                            }
                            break;
                        case "table":
                            DrawTable(g, node, x, y, width, height);
                            break;
                    }

                }
            }
            /// <summary>
            /// 绘制线条节点（横线或竖线）
            /// </summary>
            private void DrawLine(Graphics g, JObject node, float x, float y, float width, float height)
            {
                // 从 node.line 对象中读取配置
                string lineType = GetValue<string>(node, "line.type", "horizontal"); // 'horizontal' 或 'vertical'
                string align = GetValue<string>(node, "line.align", "middle"); // 对齐方式
                string colorHex = GetValue<string>(node, "line.color", "#000000");
                float lineWidthMm = GetValue<float>(node, "line.width", 0.5f); // 线条宽度 (mm)
                string lineStyle = GetValue<string>(node, "line.style", "solid"); // 'solid' 或 'dashed'

                Color color = ColorTranslator.FromHtml(colorHex);
                float lineWidthPx = lineWidthMm; // GraphicsUnit 为毫米，无需转换

                using (Pen pen = new Pen(color, lineWidthPx))
                {
                    // 设置虚线样式
                    if (lineStyle == "dashed")
                    {
                        float dashLength = 3f; // 虚线段长度 (mm)
                        float gapLength = 2f;  // 间隙长度 (mm)
                        pen.DashPattern = new float[] { dashLength, gapLength };
                    }

                    float startX, startY, endX, endY;

                    if (lineType == "vertical")
                    {
                        // 绘制竖线
                        float centerX = x + width / 2; // 默认居中
                        if (align == "left")
                        {
                            centerX = x; // 靠左
                        }
                        else if (align == "right")
                        {
                            centerX = x + width; // 靠右
                        }
                        startX = centerX;
                        startY = y;
                        endX = centerX;
                        endY = y + height;
                    }
                    else
                    {
                        // 默认或 'horizontal'：绘制横线
                        float centerY = y + height / 2; // 默认居中
                        if (align == "top")
                        {
                            centerY = y; // 顶端
                        }
                        else if (align == "bottom")
                        {
                            centerY = y + height; // 底端
                        }
                        startX = x;
                        startY = centerY;
                        endX = x + width;
                        endY = centerY;
                    }

                    // 绘制线条
                    g.DrawLine(pen, startX, startY, endX, endY);
                }
            }
            /// <summary>
            /// 绘制图片节点（支持 Base64, URL, 本地路径）
            /// </summary>
            private void DrawImage(Graphics g, JObject node, string imageUrl, float x, float y, float width, float height)
            {
                if (string.IsNullOrEmpty(imageUrl))
                {
                    DrawPlaceholder(g, "无图片", x, y, width, height);
                    return;
                }

                Image img = null;
                try
                {
                    // 1. 判断图片数据类型
                    if (imageUrl.StartsWith("data:image", StringComparison.OrdinalIgnoreCase))
                    {
                        // 处理 Base64 格式
                        string base64Data = imageUrl.Split(',')[1]; // 分割出 Base64 数据部分
                        byte[] imageBytes = Convert.FromBase64String(base64Data);
                        // 👇 关键修复：创建一个全新的 Bitmap，复制所有数据
                        using (MemoryStream ms = new MemoryStream(imageBytes))
                        {
                            using (Image originalImage = Image.FromStream(ms))
                            {
                                img = new Bitmap(originalImage); // 创建新 Bitmap，完全拥有数据
                            }
                        }
                    }
                    else if (Uri.TryCreate(imageUrl, UriKind.Absolute, out Uri uriResult) && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps))
                    {
                        // 处理网络 URL
                        using (WebClient client = new WebClient())
                        {
                            byte[] imageBytes = client.DownloadData(imageUrl);
                            // 👇 关键修复：创建一个全新的 Bitmap，复制所有数据
                            using (MemoryStream ms = new MemoryStream(imageBytes))
                            {
                                using (Image originalImage = Image.FromStream(ms))
                                {
                                    img = new Bitmap(originalImage); // 创建新 Bitmap，完全拥有数据
                                }
                            }
                        }
                    }
                    else
                    {
                        // 处理本地文件路径
                        if (File.Exists(imageUrl))
                        {
                            // 👇 关键修复：创建一个全新的 Bitmap，复制所有数据
                            using (Image originalImage = Image.FromFile(imageUrl))
                            {
                                img = new Bitmap(originalImage); // 创建新 Bitmap，完全拥有数据
                            }
                        }
                        else
                        {
                            throw new FileNotFoundException($"无法找到图片文件: {imageUrl}");
                        }
                    }

                    // 2. 如果成功加载图片，进行绘制
                    if (img != null)
                    {
                        // 获取背景配置
                        string bgSize = GetValue<string>(node, "background.size", "contain");
                        string bgPosition = GetValue<string>(node, "background.position", "center");

                        RectangleF destRect = new RectangleF(x, y, width, height);
                        RectangleF srcRect = new RectangleF(0, 0, img.Width, img.Height);

                        // 根据 background.size 配置计算目标矩形
                        if (bgSize == "fill")
                        {
                            // srcRect 保持为整个图片
                        }
                        else if (bgSize == "cover")
                        {
                            float imgRatio = (float)img.Width / img.Height;
                            float destRatio = width / height;
                            if (imgRatio > destRatio)
                            {
                                float newWidth = img.Height * destRatio;
                                float offsetX = (img.Width - newWidth) / 2;
                                srcRect = new RectangleF(offsetX, 0, newWidth, img.Height);
                            }
                            else
                            {
                                float newHeight = img.Width / destRatio;
                                float offsetY = (img.Height - newHeight) / 2;
                                srcRect = new RectangleF(0, offsetY, img.Width, newHeight);
                            }
                        }
                        else
                        {
                            float imgRatio = (float)img.Width / img.Height;
                            float destRatio = width / height;
                            if (imgRatio > destRatio)
                            {
                                float newHeight = width / imgRatio;
                                float offsetY = (height - newHeight) / 2;
                                destRect = new RectangleF(x, y + offsetY, width, newHeight);
                            }
                            else
                            {
                                float newWidth = height * imgRatio;
                                float offsetX = (width - newWidth) / 2;
                                destRect = new RectangleF(x + offsetX, y, newWidth, height);
                            }
                        }

                        // 3. 绘制图片
                        g.DrawImage(img, destRect, srcRect, GraphicsUnit.Pixel);
                    }
                }
                catch (WebException webEx)
                {
                    DrawPlaceholder(g, "网络图片加载失败", x, y, width, height, $"网络错误: {webEx.Message}");
                }
                catch (UriFormatException uriEx)
                {
                    DrawPlaceholder(g, "图片URL格式错误", x, y, width, height, uriEx.Message);
                }
                catch (FileNotFoundException fileEx)
                {
                    DrawPlaceholder(g, "本地图片未找到", x, y, width, height, fileEx.Message);
                }
                catch (Exception ex)
                {
                    DrawPlaceholder(g, "图片加载失败", x, y, width, height, ex.Message);
                }
                finally
                {
                    // 4. 清理资源
                    if (img != null)
                    {
                        img.Dispose();
                    }
                }
            }
            /// <summary>
            /// 绘制表格节点（支持自动行高）
            /// </summary>
            private void DrawTable(Graphics g, JObject node, float x, float y, float width, float height)
            {
                JArray columns = node["columns"] as JArray;
                string rawText = GetValue<string>(node, "text", "");
                JArray dataRows = GetValue<JArray>(this.template.data, rawText, null); // 获取数据行
                if (columns == null || columns.Count == 0) return;

                // ========== 1. 获取配置参数 ==========
                // 行高配置
                float headerHeightMm = GetValue<float>(node, "rowsHeight.header", 8f); // 表头行高
                float defaultDataRowHeightMm = GetValue<float>(node, "rowsHeight.data", 6f);  // 默认数据行高
                bool isRowAutoHeight = GetValue<bool>(node, "rowAutoHeight", false); // 是否启用自动行高
                                                                                     // 单元格内边距配置
                float cellPaddingMm = GetValue<float>(node, "cellPadding", 0.2f); // 默认 0.2mm
                                                                                  // 字体配置
                                                                                  // 表头字体
                float headerFontSizePt = GetValue<float>(node, "font.header.size", 12f);
                string headerFontFamily = GetValue<string>(node, "font.header.family", "Arial");
                bool headerIsBold = GetValue<bool>(node, "font.header.bold", true);
                bool headerIsItalic = GetValue<bool>(node, "font.header.italic", false);
                string headerTextColorHex = GetValue<string>(node, "color.header.text", "#000000");
                // 数据行字体
                float dataFontSizePt = GetValue<float>(node, "font.cell.size", 10f);
                string dataFontFamily = GetValue<string>(node, "font.cell.family", "Arial");
                bool dataIsBold = GetValue<bool>(node, "font.cell.bold", false);
                bool dataIsItalic = GetValue<bool>(node, "font.cell.italic", false);
                string dataTextColorHex = GetValue<string>(node, "color.cell.text", "#000000");

                FontStyle headerFontStyle = FontStyle.Regular;
                if (headerIsBold) headerFontStyle |= FontStyle.Bold;
                if (headerIsItalic) headerFontStyle |= FontStyle.Italic;
                Color headerTextColor = ColorTranslator.FromHtml(headerTextColorHex);

                FontStyle dataFontStyle = FontStyle.Regular;
                if (dataIsBold) dataFontStyle |= FontStyle.Bold;
                if (dataIsItalic) dataFontStyle |= FontStyle.Italic;
                Color dataTextColor = ColorTranslator.FromHtml(dataTextColorHex);

                // 计算总配置宽度 (毫米)
                float totalConfiguredWidthMm = 0;
                foreach (JObject col in columns)
                {
                    totalConfiguredWidthMm += GetValue<float>(col, "width", 0f);
                }
                // 计算缩放因子，确保列宽总和等于表格净宽度
                float scale = totalConfiguredWidthMm > 0 ? width / totalConfiguredWidthMm : 1f;

                // ========== 2. 计算列的像素宽度（用于绘制网格线） ==========
                List<float> columnWidthsPx = new List<float>();
                foreach (JObject column in columns)
                {
                    float colWidthMm = GetValue<float>(column, "width", 0f);
                    float colWidthPx = colWidthMm * scale;
                    columnWidthsPx.Add(colWidthPx);
                }

                // ========== 3. 获取分页状态 ==========
                int startRowIndex = GetValue<int>(node, "printState.currentRowIndex", 0);
                int totalRows = dataRows?.Count ?? 0;

                // ========== 4. 绘制表头文本和背景 ==========
                float currentY = y;
                float currentX = x;
                int colIndex = 0;
                foreach (JObject column in columns)
                {
                    float colWidthPx = columnWidthsPx[colIndex];
                    string title = GetValue<string>(column, "title", "");

                    // 绘制列背景（复用列的背景色配置）
                    if (GetValue<bool>(column, "color.enabled", false))
                    {
                        string bgColorHex = GetValue<string>(column, "color.background", "#ffffff");
                        Color bgColor = ColorTranslator.FromHtml(bgColorHex);
                        using (SolidBrush bgBrush = new SolidBrush(bgColor))
                        {
                            g.FillRectangle(bgBrush, currentX, currentY, colWidthPx, headerHeightMm);
                        }
                    }

                    // 绘制标题文本
                    if (!string.IsNullOrEmpty(title))
                    {
                        using (Font font = new Font(headerFontFamily, headerFontSizePt, headerFontStyle))
                        using (SolidBrush brush = new SolidBrush(headerTextColor))
                        {
                            // 读取并应用列的对齐方式
                            string align = GetValue<string>(column, "align", "center");
                            StringAlignment stringAlign = StringAlignment.Center;
                            switch (align)
                            {
                                case "left":
                                    stringAlign = StringAlignment.Near;
                                    break;
                                case "right":
                                    stringAlign = StringAlignment.Far;
                                    break;
                                case "center":
                                default:
                                    stringAlign = StringAlignment.Center;
                                    break;
                            }
                            StringFormat format = new StringFormat
                            {
                                Alignment = stringAlign,
                                LineAlignment = StringAlignment.Center
                            };

                            // 创建带 padding 的绘制区域
                            RectangleF cellRect = new RectangleF(
                                currentX + cellPaddingMm, // X 坐标增加 padding
                                currentY + cellPaddingMm, // Y 坐标增加 padding
                                colWidthPx - 2 * cellPaddingMm, // 宽度减去左右 padding
                                headerHeightMm - 2 * cellPaddingMm // 高度减去上下 padding
                            );
                            g.DrawString(title, font, brush, cellRect, format);
                        }
                    }
                    currentX += colWidthPx;
                    colIndex++;
                }

                // ========== 5. 绘制数据行文本和背景 ==========
                currentY += headerHeightMm; // 移动到数据区起始Y坐标
                int rowCount = 0; // 本页绘制的行数

                // --- 新增：用于测量文本高度的辅助字体 ---
                using (Font measureFont = new Font(dataFontFamily, dataFontSizePt, dataFontStyle))
                {
                    if (dataRows != null && startRowIndex < totalRows)
                    {
                        // 从 startRowIndex 开始绘制
                        for (int dataIndex = startRowIndex; dataIndex < totalRows; dataIndex++)
                        {
                            JObject dataRow = dataRows[dataIndex] as JObject;
                            if (dataRow == null) continue;

                            float dynamicRowHeight = defaultDataRowHeightMm; // 默认行高
                            List<string> cellTexts = new List<string>(); // 存储当前行所有单元格的文本，用于测量

                            // --- 第一步：收集当前行所有单元格的文本 ---
                            for (int i = 0; i < columns.Count; i++)
                            {
                                JObject column = columns[i] as JObject;
                                string field = GetValue<string>(column, "field", ""); // 从列配置中获取数据字段名
                                string cellText = "";
                                // 根据字段名从数据行中获取值
                                if (!string.IsNullOrEmpty(field) && dataRow[field] != null)
                                {
                                    cellText = dataRow[field].ToString();
                                }
                                cellTexts.Add(cellText);
                            }

                            // --- 第二步：如果启用自动行高，测量每个单元格所需高度 ---
                            if (isRowAutoHeight)
                            {
                                float maxHeight = 0f;
                                for (int i = 0; i < columns.Count; i++)
                                {
                                    string cellText = cellTexts[i];
                                    float colWidthPx = columnWidthsPx[i];

                                    if (!string.IsNullOrEmpty(cellText))
                                    {
                                        // 创建考虑了 padding 的可用宽度
                                        float availableWidth = colWidthPx - 2 * cellPaddingMm;
                                        // 使用 Graphics.MeasureString 测量文本在指定宽度下的高度
                                        // 注意：这里我们假设文本是单行的，如果是多行文本，需要更复杂的逻辑
                                        SizeF textSize = g.MeasureString(cellText, measureFont, (int)availableWidth, StringFormat.GenericTypographic);
                                        // 高度需要加上 padding
                                        float requiredHeight = textSize.Height + 2 * cellPaddingMm;
                                        if (requiredHeight > maxHeight)
                                        {
                                            maxHeight = requiredHeight;
                                        }
                                    }
                                }
                                // 如果测量出的高度大于默认高度，则使用测量值
                                if (maxHeight > defaultDataRowHeightMm)
                                {
                                    dynamicRowHeight = maxHeight;
                                }
                            }

                            // --- 第三步：检查是否超出页面高度 ---
                            if (currentY + dynamicRowHeight > y + height)
                            {
                                // 超出页面，停止绘制，设置分页标志
                                break;
                            }

                            // --- 第四步：绘制当前行的所有单元格 ---
                            currentX = x; // 每行从最左侧开始
                            for (int i = 0; i < columns.Count; i++)
                            {
                                JObject column = columns[i] as JObject;
                                float colWidthPx = columnWidthsPx[i];
                                string cellText = cellTexts[i];

                                // 绘制数据单元格背景
                                if (GetValue<bool>(column, "color.enabled", false))
                                {
                                    string bgColorHex = GetValue<string>(column, "color.background", "#ffffff");
                                    Color bgColor = ColorTranslator.FromHtml(bgColorHex);
                                    using (SolidBrush bgBrush = new SolidBrush(bgColor))
                                    {
                                        g.FillRectangle(bgBrush, currentX, currentY, colWidthPx, dynamicRowHeight);
                                    }
                                }

                                // 绘制单元格文本
                                if (!string.IsNullOrEmpty(cellText))
                                {
                                    using (Font font = new Font(dataFontFamily, dataFontSizePt, dataFontStyle))
                                    using (SolidBrush brush = new SolidBrush(dataTextColor))
                                    {
                                        // 读取并应用列的对齐方式
                                        string align = GetValue<string>(column, "align", "center");
                                        StringAlignment stringAlign = StringAlignment.Center;
                                        switch (align)
                                        {
                                            case "left":
                                                stringAlign = StringAlignment.Near;
                                                break;
                                            case "right":
                                                stringAlign = StringAlignment.Far;
                                                break;
                                            case "center":
                                            default:
                                                stringAlign = StringAlignment.Center;
                                                break;
                                        }
                                        StringFormat format = new StringFormat
                                        {
                                            Alignment = stringAlign,
                                            LineAlignment = StringAlignment.Center
                                        };

                                        // 创建带 padding 的绘制区域
                                        RectangleF cellRect = new RectangleF(
                                            currentX + cellPaddingMm, // X 坐标增加 padding
                                            currentY + cellPaddingMm, // Y 坐标增加 padding
                                            colWidthPx - 2 * cellPaddingMm, // 宽度减去左右 padding
                                            dynamicRowHeight - 2 * cellPaddingMm // 高度减去上下 padding
                                        );
                                        g.DrawString(cellText, font, brush, cellRect, format);
                                    }
                                }
                                currentX += colWidthPx;
                            }

                            // 移动到下一行
                            currentY += dynamicRowHeight;
                            rowCount++;
                            // 更新结束行索引
                            int endRowIndex = dataIndex + 1;

                            // ========== 6. 更新分页状态和全局标志 ==========
                            // 更新节点状态
                            if (node["printState"] == null)
                            {
                                node["printState"] = new JObject();
                            }
                            node["printState"]["currentRowIndex"] = endRowIndex;
                            // 关键：如果还有数据未打印，设置全局分页标志
                            if (endRowIndex < totalRows)
                            {
                                _hasNextPage = true;
                            }
                        }
                    }
                }

                // ========== 7. 统一绘制表格网格线 ==========
                bool isBorderEnabled = GetValue<bool>(node, "border.enabled", true);
                float outerBorderWidthMm = GetValue<float>(node, "border.width", 0.5f); // 外边框粗细
                float innerBorderWidthMm = GetValue<float>(node, "border.innerWidth", 0.2f); // 内部线粗细
                string borderColorHex = GetValue<string>(node, "border.color", "#000000");
                string borderStyle = GetValue<string>(node, "border.style", "solid");
                Color gridColor = ColorTranslator.FromHtml(borderColorHex);

                if (isBorderEnabled)
                {
                    using (Pen gridPen = new Pen(gridColor))
                    {
                        // 设置虚线样式
                        if (borderStyle == "dashed")
                        {
                            float dashLength = GetValue<float>(node, "border.dashLength", 3f);
                            float gapLength = GetValue<float>(node, "border.gapLength", 2f);
                            gridPen.DashPattern = new float[] { dashLength, gapLength };
                        }

                        // 绘制所有垂直线
                        float verticalX = x;
                        for (int i = 0; i <= columns.Count; i++)
                        {
                            if (i != 0 && i != columns.Count)
                            {
                                float lineWidth = innerBorderWidthMm;
                                gridPen.Width = lineWidth;
                                g.DrawLine(gridPen, verticalX, y, verticalX, y + height);
                            }
                            if (i < columns.Count)
                            {
                                verticalX += columnWidthsPx[i];
                            }
                        }

                        // 绘制所有水平线
                        float horizontalY = y;
                        // 表头底部线 (使用外边框粗细)
                        horizontalY += headerHeightMm;
                        gridPen.Width = outerBorderWidthMm;
                        g.DrawLine(gridPen, x, horizontalY, x + width, horizontalY);

                        // 数据行线 (使用内部线粗细)
                        // 由于行高现在是动态的，我们无法预先知道有多少行，所以需要在绘制完所有行后再绘制水平线
                        // 但为了简化，我们可以只绘制表头线和表格底部线，或者在每次绘制完一行后记录Y坐标
                        // 这里采用一个简单方案：只绘制表头线和表格底部线，内部线不绘制（或者您可以根据 rowCount 重新计算）
                        // 如果您需要精确的内部线，需要在步骤5中记录每一行的 bottom Y 坐标，然后在此处遍历绘制。

                        // 底部边框 (使用外边框粗细)
                        gridPen.Width = outerBorderWidthMm;
                        g.DrawLine(gridPen, x, y + height, x + width, y + height);
                    }
                }
            }
            /// <summary>
            /// 绘制条形码
            /// </summary>
            private void DrawBarcode(Graphics g, JObject node, string content, float x, float y, float width, float height)
            {
                if (string.IsNullOrEmpty(content)) return;

                try
                {
                    // 获取条形码配置
                    bool displayValue = GetValue<bool>(node, "displayValue", false); // 是否在条形码下方显示文本

                    // 获取旋转和镜像配置
                    int rotate = GetValue<int>(node, "barcode.rotate", 0); // 旋转角度 (0, 90, 180, 270)
                    bool mirror = GetValue<bool>(node, "barcode.mirror", false); // 是否镜像
                    int mrotate = rotate;
                    // 关键：移动坐标系原点到目标区域的中心
                    float centerX = x + width / 2;
                    float centerY = y + height / 2;
                    g.TranslateTransform(centerX, centerY);

                    // 应用镜像（水平翻转）
                    if (mirror)
                    {
                        mrotate += 180;
                    }

                    // 应用旋转
                    g.RotateTransform(mrotate);

                    // 🛠️ 关键修复：根据旋转角度，确定逻辑上的宽度和高度
                    // 对于 90 和 270 度旋转，逻辑上的宽高需要互换
                    float logicalWidth = width;
                    float logicalHeight = height;
                    if (rotate == 90 || rotate == 270)
                    {
                        logicalWidth = height;
                        logicalHeight = width;
                    }

                    // 1. 创建条形码写入器
                    var writer = new BarcodeWriter
                    {
                        Format = BarcodeFormat.CODE_128, // 使用 CODE128 格式
                        Options = new EncodingOptions
                        {
                            // 设置条形码的高度（像素），宽度会根据内容自动调整
                            Height = (int)(logicalHeight * 96 / 25.4f), // 简单转换，实际高度由 DrawImage 控制
                            Width = (int)(logicalWidth * 96 / 25.4f),
                            PureBarcode = !displayValue, // 注意：PureBarcode为true时不显示文本
                            Margin = 0 // 设置边距为0，让条码填满可用空间
                        }
                    };

                    // 2. 生成条形码位图
                    using (var bitmap = writer.Write(content))
                    {
                        // 3. 将位图绘制到指定的矩形区域内
                        //    DrawImage 会自动将图片缩放以适应目标矩形
                        // 🛠️ 修正：绘制真实的条码图片，使用逻辑宽高
                        g.DrawImage(bitmap, -logicalWidth / 2, -logicalHeight / 2, logicalWidth, logicalHeight);
                    }

                    // 恢复坐标变换
                    g.ResetTransform();
                }
                catch (Exception ex)
                {
                    // 绘制错误占位符
                    DrawPlaceholder(g, "条形码生成失败", x, y, width, height, ex.Message);
                }
            }
            /// <summary>
            /// 绘制二维码
            /// </summary>
            private void DrawQrCode(Graphics g, JObject node, string content, float x, float y, float width, float height)
            {
                if (string.IsNullOrEmpty(content)) return;
                try
                {
                    // 🛠️ 关键：计算高DPI位图尺寸
                    // 获取 Graphics 对象的 DPI
                    float dpiX = g.DpiX;
                    float dpiY = g.DpiY;
                    // 将目标区域的毫米尺寸转换为英寸
                    float targetInchesWidth = width / 25.4f;
                    float targetInchesHeight = height / 25.4f;
                    // 计算高分辨率位图的像素尺寸 (例如，目标为 300 DPI)
                    int bitmapWidth = (int)(targetInchesWidth * dpiX);
                    int bitmapHeight = (int)(targetInchesHeight * dpiY);

                    // 1. 创建二维码写入器
                    var writer = new BarcodeWriter
                    {
                        Format = BarcodeFormat.QR_CODE,
                        Options = new QrCodeEncodingOptions
                        {
                            // 🛠️ 关键修改：使用计算出的高分辨率尺寸
                            Width = bitmapWidth,
                            Height = bitmapHeight,
                            // 设置边距
                            Margin = 0
                        }
                    };

                    // 2. 生成二维码位图
                    using (var bitmap = writer.Write(content))
                    {
                        // 3. 将位图绘制到指定的矩形区域内
                        // DrawImage 会自动将图片缩放以适应目标矩形，但由于位图本身已是高分辨率，效果会很清晰
                        g.DrawImage(bitmap, x, y, width, height);
                    }
                }
                catch (Exception ex)
                {
                    // 绘制错误占位符
                    DrawPlaceholder(g, "二维码生成失败", x, y, width, height, ex.Message);
                }
            }
            /// <summary>
            /// 绘制错误占位符
            /// </summary>
            private void DrawPlaceholder(Graphics g, string title, float x, float y, float width, float height, string errorMessage = "")
            {
                // 绘制灰色背景
                using (SolidBrush bgBrush = new SolidBrush(Color.LightGray))
                {
                    g.FillRectangle(bgBrush, x, y, width, height);
                }
                // 绘制红色边框
                using (Pen borderPen = new Pen(Color.Red, 0.5f))
                {
                    g.DrawRectangle(borderPen, x, y, width, height);
                }
                // 绘制错误文本
                string displayText = title;
                if (!string.IsNullOrEmpty(errorMessage))
                {
                    displayText += $"\n{errorMessage}";
                }
                using (Font font = new Font("Arial", 8f))
                using (SolidBrush textBrush = new SolidBrush(Color.DarkRed))
                {
                    StringFormat format = new StringFormat
                    {
                        Alignment = StringAlignment.Center,
                        LineAlignment = StringAlignment.Center
                    };
                    g.DrawString(displayText, font, textBrush, new RectangleF(x, y, width, height), format);
                }
            }
            /// <summary>
            /// 绘制标签文本（严格逐字符绘制，100% 复刻前端 JS 逻辑）
            /// </summary>
            private void DrawLabel(Graphics g, JObject node, string text, float x, float y, float width, float height)
            {
                if (string.IsNullOrEmpty(text)) return;

                // ========== 1. 获取配置参数 (严格对应 JS) ==========
                // 字体配置
                float fontSizePt = GetValue<float>(node, "font.size", 12f);
                float minFontSizePt = GetValue<float>(node, "font.minSize", 6f);
                string fontFamily = GetValue<string>(node, "font.family", "Arial");
                bool isBold = GetValue<bool>(node, "font.bold", false);
                bool isItalic = GetValue<bool>(node, "font.italic", false);
                bool useUnderline = GetValue<bool>(node, "font.underline", false);

                // 文本格式配置
                JObject stringFormat = node["stringFormat"] as JObject ?? new JObject();
                bool isVertical = GetValue<bool>(node, "stringFormat.vertical", false);
                string direction = GetValue<string>(node, "stringFormat.direction", "ltr");
                string align = GetValue<string>(node, "stringFormat.align", "center");
                string valign = GetValue<string>(node, "stringFormat.valign", "middle");
                float lineHeightFactor = GetValue<float>(node, "stringFormat.lineHeight", 1f);
                float letterSpacing = GetValue<float>(node, "stringFormat.letterSpacing", 1f);
                bool useEllipsis = GetValue<string>(node, "stringFormat.trim", "") == "ellipsis";
                bool isOneLine = GetValue<bool>(node, "oneLine", false);

                // 颜色配置
                string textColorHex = GetValue<string>(node, "color.text", "#000000");
                Color textColor = ColorTranslator.FromHtml(textColorHex);

                // ✅ 关键：将 pt 转换为 mm
                float fontSizeMM = PointsToMillimeters(fontSizePt);
                float minFontMM = PointsToMillimeters(minFontSizePt);

                // 创建字体和画笔
                FontStyle fontStyle = FontStyle.Regular;
                if (isBold) fontStyle |= FontStyle.Bold;
                if (isItalic) fontStyle |= FontStyle.Italic;
                if (useUnderline) fontStyle |= FontStyle.Underline;

                using (Font font = new Font(fontFamily, fontSizeMM, fontStyle, GraphicsUnit.Millimeter))
                using (SolidBrush brush = new SolidBrush(textColor))
                using (Pen underlinePen = new Pen(textColor, Math.Max(0.1f, fontSizeMM * 0.08f))) // 下划线粗细也用毫米
                {
                    // 保存绘图状态
                    GraphicsState state = g.Save();
                    g.SetClip(new RectangleF(x, y, width, height));

                    // ========== 2. 测量基础尺寸 (严格对应 JS) ==========
                    float initialPx = fontSizeMM; // 对应 JS 的 this.ptToPx(fontSizePt)
                    float minFontPx = minFontMM;  // 对应 JS 的 this.ptToPx(minFontSizePt)
                    float lineHeight = initialPx * lineHeightFactor;

                    // ✅ 关键：正确计算基线偏移（使用字体的上升值）
                    float ascent = font.FontFamily.GetCellAscent(font.Style) * font.Size / font.FontFamily.GetEmHeight(font.Style);
                    float descent = font.FontFamily.GetCellDescent(font.Style) * font.Size / font.FontFamily.GetEmHeight(font.Style);
                    float baselineOffset = ascent; // 使用字体的上升值作为基线偏移

                    // ========== 3. 字符测量和缓存 (严格对应 JS) ==========
                    List<CharInfo> chars = new List<CharInfo>();
                    foreach (char c in text)
                    {
                        SizeF size = g.MeasureString(c.ToString(), font, new PointF(0, 0), StringFormat.GenericTypographic);
                        chars.Add(new CharInfo
                        {
                            Char = c.ToString(),
                            Width = size.Width,
                            Height = initialPx // JS 中高度直接使用 initialPx (毫米)
                        });
                    }
                    float totalWidth = chars.Sum(c => c.Width);

                    // ========== 4. 排版逻辑 (严格对应 JS) ==========
                    List<TextLine> textLines = new List<TextLine>();

                    if (isVertical)
                    {
                        // ========== 竖排排版逻辑 (严格对应 JS) ==========
                        float columnHeight = 0;
                        List<CharInfo> currentColumn = new List<CharInfo>();
                        foreach (CharInfo chr in chars)
                        {
                            float charHeight = initialPx * letterSpacing;
                            if (columnHeight + charHeight <= height)
                            {
                                currentColumn.Add(chr);
                                columnHeight += charHeight;
                            }
                            else
                            {
                                int newColumnCount = textLines.Count + 1;
                                if (newColumnCount * initialPx > width)
                                {
                                    if (useEllipsis)
                                    {
                                        if (textLines.Count > 0)
                                        {
                                            TextLine lastColumn = textLines[textLines.Count - 1];
                                            float ellipsisHeight = initialPx;
                                            float availableSpace = height - lastColumn.Height;
                                            if (availableSpace >= ellipsisHeight)
                                            {
                                                lastColumn.Chars.Add(new CharInfo
                                                {
                                                    Char = "...",
                                                    Width = initialPx,
                                                    Height = ellipsisHeight
                                                });
                                                lastColumn.Height += ellipsisHeight;
                                            }
                                            else
                                            {
                                                float remainingHeight = ellipsisHeight;
                                                int i = lastColumn.Chars.Count - 1;
                                                while (i >= 0 && remainingHeight > 0)
                                                {
                                                    float charHeightVal = lastColumn.Chars[i].Height * letterSpacing;
                                                    remainingHeight -= charHeightVal;
                                                    i--;
                                                }
                                                lastColumn.Chars.RemoveRange(i + 1, lastColumn.Chars.Count - i - 1);
                                                lastColumn.Chars.Add(new CharInfo
                                                {
                                                    Char = "...",
                                                    Width = initialPx,
                                                    Height = ellipsisHeight
                                                });
                                                lastColumn.Height = lastColumn.Chars.Sum(c => c.Height * letterSpacing);
                                            }
                                        }
                                        else
                                        {
                                            textLines.Add(new TextLine
                                            {
                                                Width = initialPx,
                                                Height = initialPx,
                                                Chars = new List<CharInfo>
                                    {
                                        new CharInfo
                                        {
                                            Char = "...",
                                            Width = initialPx,
                                            Height = initialPx
                                        }
                                    }
                                            });
                                        }
                                    }
                                    break;
                                }
                                textLines.Add(new TextLine
                                {
                                    Width = initialPx,
                                    Height = columnHeight,
                                    Chars = new List<CharInfo>(currentColumn)
                                });
                                currentColumn = new List<CharInfo> { chr };
                                columnHeight = charHeight;
                            }
                        }
                        if (currentColumn.Count > 0)
                        {
                            int finalColumnCount = textLines.Count + 1;
                            if (finalColumnCount * initialPx <= width)
                            {
                                textLines.Add(new TextLine
                                {
                                    Width = initialPx,
                                    Height = columnHeight,
                                    Chars = new List<CharInfo>(currentColumn)
                                });
                            }
                            else if (useEllipsis && textLines.Count > 0)
                            {
                                TextLine lastColumn = textLines[textLines.Count - 1];
                                float ellipsisHeight = initialPx;
                                float availableSpace = height - lastColumn.Height;
                                if (availableSpace >= ellipsisHeight)
                                {
                                    lastColumn.Chars.Add(new CharInfo
                                    {
                                        Char = "...",
                                        Width = initialPx,
                                        Height = ellipsisHeight
                                    });
                                    lastColumn.Height += ellipsisHeight;
                                }
                                else
                                {
                                    float remainingHeight = ellipsisHeight;
                                    int i = lastColumn.Chars.Count - 1;
                                    while (i >= 0 && remainingHeight > 0)
                                    {
                                        float charHeightVal = lastColumn.Chars[i].Height * letterSpacing;
                                        remainingHeight -= charHeightVal;
                                        i--;
                                    }
                                    lastColumn.Chars.RemoveRange(i + 1, lastColumn.Chars.Count - i - 1);
                                    lastColumn.Chars.Add(new CharInfo
                                    {
                                        Char = "...",
                                        Width = initialPx,
                                        Height = ellipsisHeight
                                    });
                                    lastColumn.Height = lastColumn.Chars.Sum(c => c.Height * letterSpacing);
                                }
                            }
                        }
                    }
                    else
                    {
                        // ========== 横排排版逻辑 (严格对应 JS) ==========
                        if (!isOneLine)
                        {
                            float lineWidth = 0;
                            List<CharInfo> currentLine = new List<CharInfo>();
                            foreach (CharInfo chr in chars)
                            {
                                float charWidth = chr.Width * letterSpacing;
                                if (lineWidth + charWidth <= width)
                                {
                                    currentLine.Add(chr);
                                    lineWidth += charWidth;
                                }
                                else
                                {
                                    int newLineCount = textLines.Count + 1;
                                    if (newLineCount * lineHeight > height)
                                    {
                                        if (useEllipsis)
                                        {
                                            if (textLines.Count > 0)
                                            {
                                                TextLine lastLine = textLines[textLines.Count - 1];
                                                SizeF ellipsisSize = g.MeasureString("...", font, new PointF(0, 0), StringFormat.GenericTypographic);
                                                float ellipsisWidth = ellipsisSize.Width;
                                                float availableSpace = width - lastLine.Width;
                                                if (availableSpace >= ellipsisWidth)
                                                {
                                                    lastLine.Chars.Add(new CharInfo
                                                    {
                                                        Char = "...",
                                                        Width = ellipsisWidth,
                                                        Height = initialPx
                                                    });
                                                    lastLine.Width += ellipsisWidth;
                                                }
                                                else
                                                {
                                                    float remainingWidth = ellipsisWidth;
                                                    int i = lastLine.Chars.Count - 1;
                                                    while (i >= 0 && remainingWidth > 0)
                                                    {
                                                        float charWidthVal = lastLine.Chars[i].Width * letterSpacing;
                                                        remainingWidth -= charWidthVal;
                                                        i--;
                                                    }
                                                    lastLine.Chars.RemoveRange(i + 1, lastLine.Chars.Count - i - 1);
                                                    lastLine.Chars.Add(new CharInfo
                                                    {
                                                        Char = "...",
                                                        Width = ellipsisWidth,
                                                        Height = initialPx
                                                    });
                                                    lastLine.Width = lastLine.Chars.Sum(c => c.Width * letterSpacing);
                                                }
                                            }
                                            else
                                            {
                                                SizeF ellipsisSize = g.MeasureString("...", font, new PointF(0, 0), StringFormat.GenericTypographic);
                                                textLines.Add(new TextLine
                                                {
                                                    Width = ellipsisSize.Width,
                                                    Chars = new List<CharInfo>
                                        {
                                            new CharInfo
                                            {
                                                Char = "...",
                                                Width = ellipsisSize.Width,
                                                Height = initialPx
                                            }
                                        }
                                                });
                                            }
                                        }
                                        break;
                                    }
                                    textLines.Add(new TextLine
                                    {
                                        Width = lineWidth,
                                        Chars = new List<CharInfo>(currentLine)
                                    });
                                    currentLine = new List<CharInfo> { chr };
                                    lineWidth = charWidth;
                                }
                            }
                            if (currentLine.Count > 0)
                            {
                                int finalLineCount = textLines.Count + 1;
                                if (finalLineCount * lineHeight <= height)
                                {
                                    textLines.Add(new TextLine
                                    {
                                        Width = lineWidth,
                                        Chars = new List<CharInfo>(currentLine)
                                    });
                                }
                                else if (useEllipsis && textLines.Count > 0)
                                {
                                    TextLine lastLine = textLines[textLines.Count - 1];
                                    SizeF ellipsisSize = g.MeasureString("...", font, new PointF(0, 0), StringFormat.GenericTypographic);
                                    float ellipsisWidth = ellipsisSize.Width;
                                    float availableSpace = width - lastLine.Width;
                                    if (availableSpace >= ellipsisWidth)
                                    {
                                        lastLine.Chars.Add(new CharInfo
                                        {
                                            Char = "...",
                                            Width = ellipsisWidth,
                                            Height = initialPx
                                        });
                                        lastLine.Width += ellipsisWidth;
                                    }
                                    else
                                    {
                                        float remainingWidth = ellipsisWidth;
                                        int i = lastLine.Chars.Count - 1;
                                        while (i >= 0 && remainingWidth > 0)
                                        {
                                            float charWidthVal = lastLine.Chars[i].Width * letterSpacing;
                                            remainingWidth -= charWidthVal;
                                            i--;
                                        }
                                        lastLine.Chars.RemoveRange(i + 1, lastLine.Chars.Count - i - 1);
                                        lastLine.Chars.Add(new CharInfo
                                        {
                                            Char = "...",
                                            Width = ellipsisWidth,
                                            Height = initialPx
                                        });
                                        lastLine.Width = lastLine.Chars.Sum(c => c.Width * letterSpacing);
                                    }
                                }
                            }
                        }
                    }

                    // ========== 5. 绘制逻辑 (严格对应 JS) ==========
                    if (isVertical)
                    {
                        // ========== 竖排绘制逻辑 (严格对应 JS) ==========
                        int columnIndex = 0;
                        int totalColumns = textLines.Count;
                        float totalColumnsWidth = totalColumns * initialPx;
                        foreach (TextLine column in textLines)
                        {
                            float offsetX = x;
                            float offsetY = y;

                            // 水平对齐
                            switch (align)
                            {
                                case "left":
                                    offsetX = x;
                                    break;
                                case "right":
                                    offsetX = x + width - totalColumnsWidth;
                                    break;
                                case "center":
                                default:
                                    offsetX = x + width / 2 - totalColumnsWidth / 2;
                                    break;
                            }

                            // 书写方向
                            switch (direction)
                            {
                                case "rtl":
                                    offsetX += (totalColumns - columnIndex - 1) * initialPx;
                                    break;
                                case "ltr":
                                default:
                                    offsetX += columnIndex * initialPx;
                                    break;
                            }

                            // 垂直对齐
                            switch (valign)
                            {
                                case "top":
                                    offsetY = y;
                                    break;
                                case "bottom":
                                    offsetY = y + height - column.Height;
                                    break;
                                case "middle":
                                default:
                                    offsetY = y + height / 2 - column.Height / 2;
                                    break;
                            }

                            float startY = offsetY;
                            foreach (CharInfo charInfo in column.Chars)
                            {
                                string displayChar = charInfo.Char;
                                if (displayChar == "." && column.Chars.Count == 1)
                                {
                                    displayChar = "...";
                                }

                                // ✅ 修正：baselineOffset 应该加到 Y 坐标上，而不是 X 坐标上
                                // offsetY 是当前字符“盒子”的顶部，加上 baselineOffset 得到基线位置
                                float charBaselineY = offsetY + baselineOffset;

                                char[] charArray = displayChar.ToCharArray();
                                foreach (char singleChar in charArray)
                                {
                                    // ✅ 修正：X 坐标是 offsetX，Y 坐标是 charBaselineY
                                    g.DrawString(singleChar.ToString(), font, brush, offsetX, charBaselineY);
                                }
                                offsetY += charInfo.Height * letterSpacing;
                            }
                            columnIndex++;
                        }
                    }
                    else
                    {
                        // ========== 横排绘制逻辑 (严格对应 JS) ==========
                        int lineIndex = 0;
                        foreach (TextLine line in textLines)
                        {
                            float offsetX = x;
                            float offsetY = y;

                            // 水平对齐
                            switch (align)
                            {
                                case "left":
                                    break;
                                case "right":
                                    offsetX = x + width - line.Width;
                                    break;
                                case "center":
                                default:
                                    offsetX = x + width / 2 - line.Width / 2;
                                    break;
                            }

                            // 垂直对齐
                            switch (valign)
                            {
                                case "top":
                                    // ✅ 修正：从容器顶部开始，每行增加 lineHeight
                                    offsetY = y + lineIndex * lineHeight;
                                    break;
                                case "bottom":
                                    // ✅ 修正：从容器底部向上计算
                                    offsetY = y + height - (textLines.Count - lineIndex) * lineHeight;
                                    break;
                                case "middle":
                                default:
                                    // ✅ 修正：计算总文本高度并居中
                                    float totalTextHeight = lineHeight * textLines.Count;
                                    offsetY = y + (height - totalTextHeight) / 2 + lineIndex * lineHeight;
                                    break;
                            }

                            // ✅ 关键：计算当前行的基线位置
                            // offsetY 是当前行“盒子”的顶部，加上 baselineOffset 得到基线位置
                            float lineBaselineY = offsetY;

                            float startX = offsetX;
                            foreach (CharInfo charInfo in line.Chars)
                            {
                                string displayChar = charInfo.Char;
                                if (displayChar == "." && line.Chars.Count == 1)
                                {
                                    displayChar = "...";
                                }

                                char[] charArray = displayChar.ToCharArray();
                                foreach (char singleChar in charArray)
                                {
                                    // ✅ 修正：使用 lineBaselineY 作为 Y 坐标
                                    g.DrawString(singleChar.ToString(), font, brush, offsetX, lineBaselineY);
                                    SizeF charSize = g.MeasureString(singleChar.ToString(), font, new PointF(0, 0), StringFormat.GenericTypographic);
                                    offsetX += charSize.Width * letterSpacing;
                                }
                            }
                            lineIndex++;
                        }
                    }

                    // 恢复绘图状态
                    g.Restore(state);
                }
            }

            /// <summary>
            /// 将印刷单位 'pt' (点) 转换为物理毫米 'mm'
            /// 1pt = 1/72 inch, 1 inch = 25.4 mm
            /// </summary>
            /// <param name="points">字体大小，单位为 point (pt)</param>
            /// <returns>对应的毫米值 (mm)</returns>
            public static float PointsToMillimeters(float points)
            {
                return points * 25.4f / 72f;
            }

            // 辅助类
            private class CharInfo
            {
                public string Char { get; set; }
                public float Width { get; set; }
                public float Height { get; set; }

            }

            private class TextLine
            {
                public float Width { get; set; }
                public float Height { get; set; }
                public List<CharInfo> Chars { get; set; } = new List<CharInfo>();
            }

            /// <summary>
            /// 绘制背景色
            /// </summary>
            private void DrawBackground(Graphics g, JObject node, float x, float y, float width, float height)
            {
                // 检查是否启用背景色
                if (!GetValue<bool>(node, "color.enabled", false)) return;

                string backgroundColor = GetValue<string>(node, "color.background", "#ffffff");
                Color color = ColorTranslator.FromHtml(backgroundColor);

                // 获取圆角半径
                float borderRadius = GetValue<float>(node, "border.radius", 0f);

                using (SolidBrush brush = new SolidBrush(color))
                {
                    if (borderRadius <= 0)
                    {
                        // 无圆角，直接绘制矩形
                        g.FillRectangle(brush, x, y, width, height);
                    }
                    else
                    {
                        // 有圆角，绘制圆角矩形
                        float radius = Math.Min(borderRadius, Math.Min(width / 2, height / 2));
                        using (GraphicsPath path = CreateRoundedRectangle(x, y, width, height, radius))
                        {
                            g.FillPath(brush, path);
                        }
                    }
                }
            }

            /// <summary>
            /// 绘制边框
            /// </summary>
            private RectangleF DrawBorder(Graphics g, JObject node, float x, float y, float width, float height)
            {
                // 检查是否启用边框
                if (!GetValue<bool>(node, "border.enabled", false))
                {
                    // 未启用边框，内部区域等于外部区域
                    return new RectangleF(x, y, width, height);
                }

                float borderWidth = GetValue<float>(node, "border.width", 0.5f);
                string borderColor = GetValue<string>(node, "border.color", "#000000");
                string borderStyle = GetValue<string>(node, "border.style", "solid");
                float borderRadius = GetValue<float>(node, "border.radius", 0f);

                Color color = ColorTranslator.FromHtml(borderColor);

                using (Pen pen = new Pen(color, borderWidth))
                {
                    // 设置虚线样式
                    if (borderStyle == "dashed")
                    {
                        float dashLength = GetValue<float>(node, "border.dashLength", 3f);
                        float gapLength = GetValue<float>(node, "border.gapLength", 2f);
                        pen.DashPattern = new float[] { dashLength, gapLength };
                    }

                    if (borderRadius <= 0)
                    {
                        // 无圆角，直接绘制矩形边框
                        g.DrawRectangle(pen, x, y, width, height);
                    }
                    else
                    {
                        // 有圆角，绘制圆角矩形边框
                        float radius = Math.Min(borderRadius, Math.Min(width / 2, height / 2));
                        using (GraphicsPath path = CreateRoundedRectangle(x, y, width, height, radius))
                        {
                            g.DrawPath(pen, path);
                        }
                    }
                }

                // ✅ 关键修正：计算并返回内部可用区域
                // 由于 Pen 的宽度是居中绘制的，我们需要从每边扣除 borderWidth / 2
                float halfBorder = borderWidth / 2f;
                float innerX = x + halfBorder;
                float innerY = y + halfBorder;
                float innerWidth = Math.Max(0, width - borderWidth);
                float innerHeight = Math.Max(0, height - borderWidth);

                return new RectangleF(innerX, innerY, innerWidth, innerHeight);
            }

            /// <summary>
            /// 创建圆角矩形路径
            /// </summary>
            private GraphicsPath CreateRoundedRectangle(float x, float y, float width, float height, float radius)
            {
                GraphicsPath path = new GraphicsPath();

                // 确保圆角半径不超过矩形尺寸的一半
                radius = Math.Min(radius, Math.Min(width / 2, height / 2));

                // 绘制四条圆角边
                path.AddArc(x, y, radius * 2, radius * 2, 180, 90);        // 左上角
                path.AddArc(x + width - radius * 2, y, radius * 2, radius * 2, 270, 90);  // 右上角
                path.AddArc(x + width - radius * 2, y + height - radius * 2, radius * 2, radius * 2, 0, 90);   // 右下角
                path.AddArc(x, y + height - radius * 2, radius * 2, radius * 2, 90, 90);  // 左下角

                path.CloseFigure();
                return path;
            }
            public static void ApplyQualitySettings(Graphics g, string quality)
            {
                switch (quality)
                {
                    case "Draft":
                        g.SmoothingMode = SmoothingMode.None;
                        g.TextRenderingHint = TextRenderingHint.SystemDefault;
                        g.InterpolationMode = InterpolationMode.Low;
                        g.PixelOffsetMode = PixelOffsetMode.Default;
                        break;

                    case "Standard":
                        g.SmoothingMode = SmoothingMode.HighSpeed;
                        g.TextRenderingHint = TextRenderingHint.AntiAlias;
                        g.InterpolationMode = InterpolationMode.Bilinear;
                        g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
                        break;

                    case "HighQuality":
                    case "Photo":
                    default:
                        g.SmoothingMode = SmoothingMode.AntiAlias;
                        g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
                        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        g.PixelOffsetMode = PixelOffsetMode.HighQuality;
                        break;
                }
            }
            /// <summary>
            /// 打印页面事件处理器
            /// </summary>
            private void Doc_PrintPage(object sender, PrintPageEventArgs e)
            {
                try
                {
                    _hasNextPage = false;
                    // 获取Graphics对象用于绘制
                    Graphics g = e.Graphics;
                    // 设置绘图单位为毫米（便于与前端设计保持一致）
                    g.PageUnit = GraphicsUnit.Millimeter;
                    string qualit = PrintPlug.GetConfig<string>("mainsetting.printQualit", "");
                    ApplyQualitySettings(g, qualit);
                    g.SmoothingMode = SmoothingMode.AntiAlias;          // 抗锯齿（线条/图形）
                    g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; // ClearType 高质量文本
                    g.InterpolationMode = InterpolationMode.HighQualityBicubic; // 图片缩放用高质量
                    g.PixelOffsetMode = PixelOffsetMode.HighQuality;    // 像素对齐优化
                    if (template != null && template.Template != null && template.Template.nodes != null)
                    {
                        foreach (JObject node in this.template.Template.nodes)
                        {
                            DrawNode(g, node);
                        }
                        if (!license.Value)
                        {
                            DrawWatermark(g, "未注册，仅供测试");
                        }
                    }
                    // 设置打印完成标志（单页打印）
                    e.HasMorePages = _hasNextPage;
                }
                catch (Exception ex)
                {
                    // 在打印过程中发生错误，记录到控制台
                    System.Diagnostics.Debug.WriteLine($"打印页面绘制错误: {ex.Message}");
                    e.HasMorePages = false;
                }
            }

            /// <summary>
            /// 公共方法：启动打印
            /// </summary>
            /// <returns>打印操作结果</returns>
            public YbMessage StartPrint()
            {
                return this.ExecutePrint();
            }
            /// <summary>
            /// 在指定的 Graphics 对象上绘制全页水印。
            /// 此方法设计用于在 PrintPage 事件中调用，利用 e.Graphics 的页面尺寸。
            /// </summary>
            /// <param name="g">绘图 Graphics 对象</param>
            /// <param name="watermarkText">水印文字，例如 "未注册" 或 "试用版"</param>
            /// <param name="opacity">水印透明度，范围 0.0 (完全透明) 到 1.0 (完全不透明)，建议值 0.1-0.3</param>
            /// <param name="fontSizePt">水印字体大小 (单位: point)</param>
            /// <param name="rotationAngle">水印旋转角度 (单位: 度)，例如 -45</param>
            private void DrawWatermark(Graphics g, string watermarkText, float opacity = 0.2f, float fontSizePt = 48f, float rotationAngle = -45f)
            {
                if (string.IsNullOrEmpty(watermarkText))
                    return; // 如果没有水印文字，则直接返回

                // 获取页面尺寸 (单位由 g.PageUnit 决定，通常是毫米)
                float pageWidth = g.VisibleClipBounds.Width;
                float pageHeight = g.VisibleClipBounds.Height;

                // 创建半透明画笔
                Color watermarkColor = Color.FromArgb((int)(opacity * 255), Color.Red); // 使用红色，更醒目
                using (SolidBrush brush = new SolidBrush(watermarkColor))
                using (Font font = new Font("微软雅黑", fontSizePt, FontStyle.Bold))
                {
                    // 测量单个水印文字的尺寸
                    SizeF textSize = g.MeasureString(watermarkText, font);

                    // 计算平铺间距：文字宽度和高度的1.5倍，确保有重叠和空隙
                    float spacingX = textSize.Width * 1.5f;
                    float spacingY = textSize.Height * 1.5f;

                    // 计算需要绘制的行数和列数
                    int columns = (int)(pageWidth / spacingX) + 2; // +2 确保边缘也能覆盖
                    int rows = (int)(pageHeight / spacingY) + 2;

                    // 创建一个随机数生成器，用于生成偏移量
                    Random random = new Random();

                    // 保存当前绘图状态
                    GraphicsState state = g.Save();

                    // 将原点移动到页面中心，以便围绕中心旋转
                    g.TranslateTransform(pageWidth / 2, pageHeight / 2);
                    // 应用旋转
                    g.RotateTransform(rotationAngle);
                    // 将原点移回左上角，准备平铺绘制
                    g.TranslateTransform(-pageWidth / 2, -pageHeight / 2);

                    // 开始平铺绘制
                    for (int row = 0; row < rows; row++)
                    {
                        // ✅ 关键修改：为每一行生成一个随机的水平偏移量
                        // 偏移量范围为 -spacingX/4 到 +spacingX/4，这样不会破坏整体布局
                        float rowOffsetX = (float)(random.NextDouble() - 0.5) * spacingX / 2;

                        for (int col = 0; col < columns; col++)
                        {
                            // 计算当前水印的绘制位置，并加上行偏移量
                            float x = col * spacingX - textSize.Width / 2 + rowOffsetX; // ✅ 加上行偏移
                            float y = row * spacingY - textSize.Height / 2;

                            // 绘制水印文字
                            g.DrawString(watermarkText, font, brush, x, y);
                        }
                    }

                    // 恢复绘图状态
                    g.Restore(state);
                }
            }
        }

        private static int MmToHundredthsInch(float mm)
        {
            return (int)(mm / 25.4f * 100);
        }
        public class PrinterInfo
        {
            /// <summary>
            /// 默认打印机名称
            /// </summary>
            public string defaultPrinter { get; set; }

            /// <summary>
            /// 所有打印机名称列表
            /// </summary>
            public List<string> printers { get; set; }

            public PrinterInfo()
            {
                printers = new List<string>();
            }

            public override string ToString()
            {
                return JsonConvert.SerializeObject(this, Formatting.Indented);
            }
        }
        public static List<string> GetFontsThatNameContainsChinese()
        {
            var chineseNamedFonts = new List<string>();

            // 中文字符正则：常用汉字范围（基本汉字区块）
            var chineseCharRegex = new Regex(@"[\u4e00-\u9fa5]");

            try
            {
                using (var fontCollection = new InstalledFontCollection())
                {
                    foreach (var fontFamily in fontCollection.Families)
                    {
                        string fontName = fontFamily.Name;

                        // 如果字体名中包含至少一个中文字符，则保留
                        if (chineseCharRegex.IsMatch(fontName))
                        {
                            chineseNamedFonts.Add(fontName);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"获取字体时出错: {ex.Message}");
            }

            return chineseNamedFonts.Distinct().ToList();
        }
        public static List<string> GetAllFontNames()
        {
            var fontNames = new List<string>();

            try
            {
                using (var fontCollection = new InstalledFontCollection())
                {
                    foreach (var fontFamily in fontCollection.Families)
                    {
                        fontNames.Add(fontFamily.Name);
                    }
                }
            }
            catch (Exception ex)
            {
                // 可选：记录异常或抛出
                Console.WriteLine($"获取字体列表时出错: {ex.Message}");
                // 你可以选择抛出异常或返回空列表
                // throw;
            }

            return fontNames;
        }

        /// <summary>
        /// 获取默认打印机名称和所有打印机列表
        /// </summary>
        /// <returns>包含默认打印机和打印机列表的对象</returns>
        public static PrinterInfo GetPrinterNames()
        {
            var info = new PrinterInfo();

            // 获取所有已安装打印机
            foreach (string printer in PrinterSettings.InstalledPrinters)
            {
                info.printers.Add(printer);
            }

            // 获取默认打印机（可能为空或无效，但通常系统会设一个）
            try
            {
                var defaultSettings = new PrinterSettings();
                if (!string.IsNullOrEmpty(defaultSettings.PrinterName))
                {
                    info.defaultPrinter = defaultSettings.PrinterName;
                }
                else
                {
                    info.defaultPrinter = null; // 无有效默认打印机
                }
            }
            catch (Exception)
            {
                info.defaultPrinter = null; // 获取失败
            }

            return info;
        }
        // 在 PrintPlug 类中，添加一个静态字典用于缓存模板
        private static readonly Dictionary<string, Template> _templateCache = new Dictionary<string, Template>();
        // 为缓存操作添加一个简单的锁，保证线程安全
        private static readonly object _cacheLock = new object();
        public static YbMessage Print(string message,bool waitprint=false)
        {
            try
            {
                // 注意：这里你传入的是 string message，但代码里没用它，而是 new 了一个空模板
                // 应该是反序列化 message 为 YbTemplate，但你原代码没做，这里保持原样（但逻辑错误）
                YbTemplate data = JsonConvert.DeserializeObject<YbTemplate>(message);
                if (data == null)
                    return new YbMessage(-1, "打印模板数据不能为空");

                if (data.Template == null && !string.IsNullOrEmpty(data.url))
                {
                    YbMessage fetchResult = FetchAndSetTemplateFromUrl(data);
                    if (fetchResult.code != 0)
                    {
                        return fetchResult;
                    }
                }
                if (data.Template == null)
                    return new YbMessage(-2, "模板对象不能为空");

                if (data.Template.config == null)
                    return new YbMessage(-3, "模板配置(config)不能为空");

                if (data.Template.config.width <= 0)
                    return new YbMessage(-4, $"无效的页面宽度: {data.Template.config.width} mm");

                if (data.Template.config.height <= 0)
                    return new YbMessage(-5, $"无效的页面高度: {data.Template.config.height} mm");
                if (string.IsNullOrEmpty(data.PrintName))
                    return new YbMessage(-1, "无效的打印机名称");

                printData pdata = new printData();
                pdata.doc = new PrintDocument();
                pdata.template = data;

                try
                {
                    int widthInHundredthsInch = MmToHundredthsInch(data.Template.config.width);
                    int heightInHundredthsInch = MmToHundredthsInch(data.Template.config.height);

                    if (widthInHundredthsInch <= 0 || heightInHundredthsInch <= 0)
                    {
                        return new YbMessage(-6, $"转换后的纸张尺寸无效: 宽度={widthInHundredthsInch}, 高度={heightInHundredthsInch} (单位: 百分之一英寸)");
                    }
                    string fx = "纵向纸张";
                    if (data.Template.config.orientation.ToLower() == "landscape")
                    {
                        fx = "横向纸张";
                    }
                    string pagename = "YbPrintCustomPage"+ fx + data.Template.config.width.ToString() + "MM," + data.Template.config.height.ToString() + "MM";
                    YbMessage err = Yb.CustomPaperHelper.AddCustomPaper(pdata.template.PrintName, pagename, data.Template.config.width, data.Template.config.height);
                    pdata.page = new PaperSize(pagename, widthInHundredthsInch, heightInHundredthsInch);
                    pdata.WaitForPrintCompletion = waitprint;
                    // 调用printData的打印函数
                    return pdata.StartPrint();
                }
                catch (Exception ex)
                {
                    return new YbMessage(-7, $"初始化纸张尺寸时发生错误: {ex.Message}");
                }
            }
            catch (Exception ex)
            {
                return new YbMessage(-999, $"未知错误: {ex.Message}");
            }
        }
        /// <summary>
        /// 从指定的URL获取模板数据，并赋值给 YbTemplate 对象。
        /// 此方法会优先检查内存缓存，避免重复下载。
        /// </summary>
        /// <param name="ybTemplate">要填充模板数据的 YbTemplate 对象</param>
        /// <returns>操作结果，成功则 code 为 0</returns>
        private static YbMessage FetchAndSetTemplateFromUrl(YbTemplate ybTemplate)
        {
            try
            {
                string url = ybTemplate.url;
                Template cachedTemplate = null;

                // 线程安全地从缓存中获取
                lock (_cacheLock)
                {
                    if (_templateCache.TryGetValue(url, out cachedTemplate))
                    {
                        ybTemplate.Template = cachedTemplate;
                        return YbMessage.Success("模板从缓存加载成功");
                    }
                }

                // 缓存中没有，从网络下载
                string jsonResponse;
                using (WebClient client = new WebClient())
                {
                    // 可以根据需要设置超时或请求头
                    // client.Timeout = 10000; // 10秒超时，但 WebClient 默认不支持，需用 HttpWebRequest 或 HttpClient
                    jsonResponse = client.DownloadString(url);
                }

                // 将下载的JSON字符串反序列化为 Template 对象
                Template downloadedTemplate = JsonConvert.DeserializeObject<Template>(jsonResponse);
                if (downloadedTemplate == null)
                {
                    return YbMessage.Error("从URL下载的模板数据无法解析");
                }

                // 将下载的模板存入缓存
                lock (_cacheLock)
                {
                    _templateCache[url] = downloadedTemplate;
                }

                // 将下载的模板赋值给传入的对象
                ybTemplate.Template = downloadedTemplate;

                return YbMessage.Success("模板从URL下载并缓存成功");
            }
            catch (WebException webEx)
            {
                return YbMessage.Error($"网络错误，无法从URL下载模板: {webEx.Message}");
            }
            catch (JsonException jsonEx)
            {
                return YbMessage.Error($"JSON解析错误: {jsonEx.Message}");
            }
            catch (Exception ex)
            {
                return YbMessage.Error($"从URL加载模板时发生未知错误: {ex.Message}");
            }
        }
    }
}